#ifndef __EPSILON_3D__DRIVER_H__
#define __EPSILON_3D__DRIVER_H__

#include "device.h"

/// What the hell?! This should not be needed...
#undef Always
#undef True
#undef False

class e3dRenderAPI;
class e3dRenderDevice;
class e3dRenderBackend;

class e3dDriver
{
public:
  static e3dDisplayDevices GetDevices (void);

private:
  e3dRenderDevice*  device;
  e3dRenderBackend* backend;
};

namespace Epsilon3D {
  e3dDriver* CreateDriver (e3dRenderAPI* api, e3dRenderDevice* dev);
};

namespace e3dRenderStates
{
  enum ShadingModel {
    Flat,
    Smooth,

    //Phong
  };

  enum TestFunction {
    Less,
    LessOrEqual,

    Greater,
    GreaterOrEqual,

    Equal,
    NotEqual,

    Always,
    Never
  };

  enum Winding {
    Clockwise,
    CounterClockwise
  };

  enum Side {
    Front,
    Back
  };

  enum FillMode {
    Solid,
    Wireframe,
    Point
  };

  enum Boolean {
    Enabled  = 1,
    Disabled = 0,

    True  = 1,
    False = 0,

    Yes = 1,
    No  = 0
  };

  enum HintMode {
    DontCare,
    Fastest,
    Nicest,
  };

  enum MatrixType {
    Projection = 0x01,
    ModelView  = 0x02,
    Texture    = 0x04,
  };

  enum PrimitiveType {
    Points,  
    PointSprite,

    Lines,
    LineLoop,
    LineStrip,

    Triangles,
    TriangleStrip,
    TriangleFan,

    Quads,
    QuadStrip,

    Polygon,
  };

  enum TextureType {
    Tex1D,
    Tex2D,
    Tex3D,
    CubeMap,
  };

  enum ShaderType {
    Vertex,
    Pixel
  };

  enum TextureFormat {
    RGB,
    RGBA,
    BGRA,
  };

  enum TextureCompression {
    DXT1,
    DXT3,
    DXT5,
  };

  enum BlendOp {
    Zero,
    One,
    DestColor,
    InverseDestColor,
    SourceAlpha,
    InverseSourceAlpha,
    DestAlpha,
    InverseDestAlpha,
    SourceAlphaSaturate,

    ConstantColor,
    InverseConstantColor,
    ConstantAlpha,
    InverseConstantAlpha,

    /* Destination Only */
    SourceColor,
    InverseSourceColor,
  };

  enum StateName {
    AlphaFunc,
    AlphaTest,

    Blend,
    BlendFuncSrc,
    BlendFuncDst,

    ClearColor,
    ClearDepth,
    ClearStencil,

    Color,
    ColorMaskA,
    ColorMaskB,
    ColorMaskG,
    ColorMaskR,

    DepthFunc,
    DepthTest,
    DepthMask,

    Normalization,

    PolygonOffset,
    PolygonOffsetFactor,
    PolygonOffsetUnits,

    StencilFail,
    StencilFunc,
    StencilPassDepthFail,
    StencilPassDepthPass,
    StencilTest,
  };

  enum BufferName {
    ColorBuffer   = 0x01,
    DepthBuffer   = 0x02,
    StencilBuffer = 0x04,
    AccumBuffer   = 0x08
  };

  enum AttribClass {
    ColorBufferState,  /* Alpha Test, Blending, etc... */
    /*Current,*/       // See: Immediate Mode Sub-Driver.
    DepthBufferState,
    Enable,
    /*Evaluator,*/
    /*Fog,*/
    Hint,
    /*Lighting,*/
    /*Line,*/
    /*Point,*/
    /*Polygon,*/
    /*Scissor,*/
    /*Stencil,*/
    /*Texture,*/
    /*Transform,*/
    /*Viewport,*/
  };
}

class e3dRenderState {
public:
  void SetAlphaFunc      (e3dRenderStates::TestFunction func, float ref);
  void SetAlphaTest      (e3dRenderStates::Boolean      test);
  void SetBlend          (e3dRenderStates::Boolean      blend);
  void SetBlendFunc      (e3dRenderStates::BlendOp      src,
                          e3dRenderStates::BlendOp      dest);
  void SetColorMask      (e3dRenderStates::Boolean      red,
                          e3dRenderStates::Boolean      green,
                          e3dRenderStates::Boolean      blue,
                          e3dRenderStates::Boolean      alpha);
  void SetCullFaces      (e3dRenderStates::Boolean      cull);
  void SetCullSide       (e3dRenderStates::Side         side);
  void SetDepthMask      (e3dRenderStates::Boolean      write);
  void SetFrontFace      (e3dRenderStates::Winding      face); 
  void SetFillMode       (e3dRenderStates::FillMode     mode);
  void SetPerspective    (e3dRenderStates::HintMode     mode);
  void SetClearColor     (float r, float g, float b, float a);
  void SetClearDepth     (float depth);
  void SetClearStencil   (int   stencil);
  void SetShadeModel     (e3dRenderStates::ShadingModel model);
  void SetDepthFunc      (e3dRenderStates::TestFunction func);
  void SetDepthTest      (e3dRenderStates::Boolean      test);

  void SetMatrix         (e3dRenderStates::MatrixType   type,
                          float*                        matrix);

  float* GetMatrix       (e3dRenderStates::MatrixType   type);

  /* TODO: Polygon Offset. */

  float* GetClearColor   (void);
  float  GetClearDepth   (void);
  int    GetClearStencil (void);


  void Clear             (int                           buffers);

private:
  e3dRenderStates::ShadingModel  ShadingModel;

  e3dRenderStates::TestFunction  DepthFunction;
  e3dRenderStates::Boolean       DepthTest;
  e3dRenderStates::Boolean       DepthWrite;

  e3dRenderStates::TestFunction  AlphaFunction;
  float                          AlphaRefVal;
  e3dRenderStates::Boolean       AlphaTest;

  e3dRenderStates::BlendOp       BlendSourceOp;
  e3dRenderStates::BlendOp       BlendDestOp;
  e3dRenderStates::Boolean       Blend;

  float                          ClearColor [4];
  float                          ClearDepth;
  int                            ClearStencil;

  e3dRenderStates::Boolean       ColorMaskA;
  e3dRenderStates::Boolean       ColorMaskB;
  e3dRenderStates::Boolean       ColorMaskG;
  e3dRenderStates::Boolean       ColorMaskR;

  e3dRenderStates::Boolean       CullFaces;
  e3dRenderStates::Side          CullSide;

  e3dRenderStates::Winding       FrontFace;

  e3dRenderStates::HintMode      PerspectiveCorrection;

  e3dRenderStates::Boolean       PolygonOffset;
  float                          PolygonOffsetFactor;
  float                          PolygonOffsetUnits;

  e3dRenderStates::FillMode      Fill;

  /* TODO: Add logic to enable GPU-side T&L on matrices, instead of using
             glLoadMatrix (...) all the time.

         (i.e.  LoadIdentity () --> MultiplyMatrix () --> RotateMatrix ())

               This will require a CPU/GPU synchronization to read the
                 value of a matrix computed such a way, but is generally
                   considerably quicker.
   */
  
  float                          ModelViewMatrix  [16];
  float                          ProjectionMatrix [16];
  float                          TextureMatrix    [16];
};

#endif /* __EPSILON_3D__DRIVER_H__ */
